

#include "main.h"

/******************************************************************************/


struct global_cfg gcfg;


/******************************************************************************/


void hex_dump(char *str, const void *buf, int size)
{
	int i, j;
	const u8 *ubuf = buf;

	if(str)
		printf("%s:\n", str);

	for(i=0; i<size; i+=16){
		printf("%4x: ", i);
		for(j=0; j<16; j++){
			if((i+j)<size){
				printf(" %02x", ubuf[j]);
			}else{
				printf("   ");
			}
		}
		printf("  ");
		for(j=0; j<16&&(i+j)<size; j++){
			if(ubuf[j]>=0x20 && ubuf[j]<=0x7f){
				printf("%c", ubuf[j]);
			}else{
				printf(".");
			}
		}
		printf("\n");
		ubuf += 16;
	}
	printf("\n");
}


static struct timeval t_start;
static int timer_init = 0;

u64 get_ticks(void)
{
	struct timeval now;

	if(timer_init==0){
		gettimeofday(&t_start, NULL);
		timer_init = 1;
		return 0;
	}

	gettimeofday(&now, NULL);

	return (now.tv_sec - t_start.tv_sec)*1000 + now.tv_usec/1000;
}


u16 ip_checksum(u16 *addr, int len)
{
	int nleft = len;
	int sum = 0;
	u16 *w = addr;
	u16 answer = 0;

	while(nleft > 1){
		sum += *w++;
		nleft -= 2;
	}

	if(nleft == 1) {
        *(u16*)(&answer) = *(u16*)w;
		sum += answer;
	}

	sum = (sum >> 16) + (sum & 0xffff); //高16位 + 低16位
	sum += (sum >> 16);                 //+进位
	answer = ~sum;                      //取反

	return (answer);
}


/******************************************************************************/





static EVIO *ev_header;
static int ev_del_mark;
static int ev_num;
static int ev_tmval;
static int ev_running;

static fd_set ev_fds_in;
static fd_set ev_fds_out;
static int ev_maxfd;

static pthread_mutex_t ev_mux;


static void evio_lock(void)
{
	pthread_mutex_lock(&ev_mux);
}

static void evio_unlock(void)
{
	pthread_mutex_unlock(&ev_mux);
}


int evio_init(int timeout_val)
{
	ev_running = 1;
	ev_header = NULL;
	ev_del_mark = 0;
	ev_num = 0;
	ev_tmval = timeout_val;
	ev_maxfd = 0;

	FD_ZERO(&ev_fds_in);
	FD_ZERO(&ev_fds_out);

	pthread_mutex_init(&ev_mux, NULL);

	return 0;
}


int evio_exit(void)
{
	ev_running = 0;
	return 0;
}


EVIO *evio_find(int fd)
{
	EVIO *evio = ev_header;

	while(evio){
		if(evio->fd == fd)
			return evio;
		evio = evio->next;
	}

	return NULL;
}


static void evio_find_maxfd(void)
{
	EVIO *evio = ev_header;

	ev_maxfd = 0;
	while(evio){
		if(ev_maxfd < evio->fd)
			ev_maxfd = evio->fd;
		evio = evio->next;
	}
}


int evio_add(int fd, void *read_func, void *write_func, void *tmout_func, void *arg)
{
	EVIO *evio = (EVIO*)malloc(sizeof(EVIO));

	evio_lock();

	evio->fd = fd;
	evio->flag = 0;
	evio->read_func = read_func;
	evio->write_func = write_func;
	evio->tmout_func = tmout_func;
	evio->arg = arg;
	evio->next = ev_header;
	ev_header = evio;

	evio_unlock();
	return 0;
}


int evio_enable(int fd, int flag)
{
	EVIO *evio;

	evio = evio_find(fd);
	if(evio==NULL)
		return -1;

	if(flag&EV_IN ){
		evio->flag |= EV_IN;
		FD_SET(fd, &ev_fds_in);
		if(ev_maxfd<fd) ev_maxfd = fd;
	}

	if(flag&EV_OUT){
		evio->flag |= EV_OUT;
		FD_SET(fd, &ev_fds_out);
		if(ev_maxfd<fd) ev_maxfd = fd;
	}

	if(flag&EV_TMOUT){
		evio->flag |= EV_TMOUT;
	}

	return 0;
}


int evio_disable(int fd, int flag)
{
	EVIO *evio;

	evio = evio_find(fd);
	if(evio==NULL)
		return -1;

	if(flag&EV_IN ){
		evio->flag &= ~EV_IN;
		FD_CLR(fd, &ev_fds_in);
	}
	if(flag&EV_OUT){
		evio->flag &= ~EV_OUT;
		FD_CLR(fd, &ev_fds_out);
	}
	if(flag&EV_TMOUT){
		evio->flag &= ~EV_TMOUT;
	}

	evio_find_maxfd();

	return 0;
}


int evio_del(int fd)
{
	EVIO *evio;

	evio = evio_find(fd);
	if(evio==NULL)
		return -1;
	evio->flag |= EV_DEL;

	ev_del_mark = 1;
	return 0;
}


static void evio_check_del(void)
{
	EVIO *evio, **pre;

	if(ev_del_mark==0)
		return;

	evio_lock();

	evio = ev_header;
	pre = &ev_header;

	while(evio){
		if(!(evio->flag & EV_DEL)){
			pre = &evio->next;
			evio = evio->next;
			continue;
		}

		*pre = evio->next;

		if(evio->flag&EV_IN)  FD_CLR(evio->fd, &ev_fds_in);
		if(evio->flag&EV_OUT) FD_CLR(evio->fd, &ev_fds_out);

		free(evio);

		evio = *pre;
	}

	evio_find_maxfd();

	evio_unlock();
	ev_del_mark = 0;
}


static void evio_timeout(void)
{
	void (*ev_func)(void *arg);
	EVIO *evio = ev_header;

	while(evio){
		if(evio->flag&EV_TMOUT){
			ev_func = evio->tmout_func;
			if(ev_func)
				ev_func(evio->arg);
		}
		evio = evio->next;
	}
}


int evio_loop(void *tmout_func)
{
	EVIO *evio;
	fd_set fds_in;
	fd_set fds_out;
	struct timeval tmout;
	void (*timeout)(int ms) = tmout_func;
	void (*ev_func)(void *arg);
	int retv;


	while(ev_running){
		evio_check_del();

		if(ev_maxfd==0){
			usleep(ev_tmval*1000);
			evio_timeout();
			if(timeout){
				timeout(ev_tmval);
			}
			continue;
		}

		fds_in  = ev_fds_in;
		fds_out = ev_fds_out;

		tmout.tv_sec = 0;
		tmout.tv_usec = ev_tmval*1000;
		retv = select(ev_maxfd+1, &fds_in, &fds_out, NULL, &tmout);
		if(retv<0){
			printf("EVIO: select failed! %d\n", WSAGetLastError());
			break;
		}else if(retv==0){
			evio_timeout();
			if(timeout){
				timeout(ev_tmval);
			}
			continue;
		}

		evio = ev_header;
		while(evio){
			if(evio->flag&EV_DEL)
				goto _next;

			if(FD_ISSET(evio->fd, &fds_in)){
				ev_func = evio->read_func;
				if(ev_func)
					ev_func(evio->arg);
			}
			if(FD_ISSET(evio->fd, &fds_out)){
				ev_func = evio->write_func;
				if(ev_func)
					ev_func(evio->arg);
			}
_next:
			evio = evio->next;
		}

	}

	return 0;
}


/******************************************************************************/


static void parse_addr(char *str, char *addr, int *port)
{
	char *p;

	*addr = 0;
	*port = 0;

	p = strchr(str, ':');
	if(p){
		*p = 0;
		strcpy(addr, str);
		*port = atoi(p+1);
		*p = ':';
	}else{
		p = strchr(str, '.');
		if(p){
			strcpy(addr, str);
		}else{
			*port = atoi(str);
		}
	}

}


static void show_usage(void)
{
	printf("\nUsage:\n");
	printf("\tkcp2raw {-c|-s} {-icmp|-ftcp} [-u] [-l ip:port] [-f ip:port] [-t ip:port] [-i ip]\n");
	printf("\n");
	printf("\t\t-c      client mode\n");
	printf("\t\t-s      server mode\n");
	printf("\t\t-icmp   use icmp tunnel\n");
	printf("\t\t-udp    use  udp tunnel\n");
	printf("\t\t-ftcp   use fake_tcp tunnel\n");
	printf("\t\t-u      UDP mode (default TCP mode)\n");
	printf("\t\t-l      listen address:port\n");
	printf("\t\t-f      forward address:port\n");
	printf("\t\t-t      target address:port\n");
	printf("\t\t-i      interface addrsss/name\n");
	printf("\n");
}


/******************************************************************************/



int main(int argc, char *argv[])
{
	int ap = 1;

#ifdef __WIN32__
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif

	memset(&gcfg, 0, sizeof(gcfg));
	gcfg.mtu = 1440;
	gcfg.port_mode = PORT_TCP;

	while(ap<argc){
		if(!strcmp(argv[ap], "-h")){
			show_usage();
			return 0;
		}else if(!strcmp(argv[ap], "-c")){
			gcfg.run_mode = CLIENT_MODE;
		}else if(!strcmp(argv[ap], "-s")){
			gcfg.run_mode = SERVER_MODE;
		}else if(!strcmp(argv[ap], "-l")){
			ap += 1;
			if(ap>=argc || argv[ap][0]=='-')
				goto _invalid_arg;
			parse_addr(argv[ap], gcfg.listen_ip, &gcfg.listen_port);
		}else if(!strcmp(argv[ap], "-f")){
			ap += 1;
			if(ap>=argc || argv[ap][0]=='-')
				goto _invalid_arg;
			parse_addr(argv[ap], gcfg.forward_ip, &gcfg.forward_port);
		}else if(!strcmp(argv[ap], "-t")){
			ap += 1;
			if(ap>=argc || argv[ap][0]=='-')
				goto _invalid_arg;
			parse_addr(argv[ap], gcfg.target_ip, &gcfg.target_port);
		}else if(!strcmp(argv[ap], "-i")){
			ap += 1;
			if(ap>=argc || argv[ap][0]=='-')
				goto _invalid_arg;
			strcpy(gcfg.devname, argv[ap]);
		}else if(!strcmp(argv[ap], "-icmp")){
			gcfg.raw_mode = RAWMODE_ICMP;
		}else if(!strcmp(argv[ap], "-udp")){
			gcfg.raw_mode = RAWMODE_UDP;
		}else if(!strcmp(argv[ap], "-ftcp")){
			gcfg.raw_mode = RAWMODE_FAKETCP;
		}else if(!strcmp(argv[ap], "-u")){
			gcfg.port_mode = PORT_UDP;
		}else{
			printf("Unknow arg! [%s]\n", argv[ap]);
			goto _invalid_arg;
		}

		ap += 1;
	}

	if(gcfg.listen_port && gcfg.forward_port){
		printf("Can't use -l and -f together!\n");
		return -1;
	}
	if(gcfg.listen_port==0 && gcfg.forward_port==0){
		printf("No listen or forward config!\n");
		return -1;
	}

	run_tcp_server();

_invalid_arg:
	printf("invalid arg!\n");
	show_usage();

	return 0;
}


